package edu.hd4ic.algorithm.realizations;

import edu.hd4ic.algorithm.base.AbstractHDBasedAlgorithm;

/**
 * Created with IntelliJ IDEA.
 * User: morfant
 * Date: 06.01.13
 * Time: 18:47
 * To change this template use File | Settings | File Templates.
 */
public class M_HausdorffDistance extends AbstractHDBasedAlgorithm {

    private int backGroundThreshold;

    public M_HausdorffDistance(int backGroundThreshold) {
        this.backGroundThreshold = backGroundThreshold;
    }

    @Override
    protected double calculateDirectDistance(int[] firstImageDataBlock, int[] secondImageDataBlock, int width, int height) {
        int cardinality = 0;
        double distanceSum = 0.0;
        int pixelValue;
        int i, j;

        for (i = 0; i < height; i++) {
            for (j = 0; j < width; j++) {
                pixelValue = firstImageDataBlock[i * width + j];
                if (pixelValue > 0) {
                    distanceSum += calculateDistanceFromPixelToSet(i, j, secondImageDataBlock, width, height);
                    cardinality++;
                }
            }
        }

        return distanceSum / ((double) cardinality);
    }


    private double calculateDistanceFromPixelToSet(int x, int y, int[] secondImageDataBlock, int width, int height) {
        double distance = 0.0;
        double minDistance = Double.MAX_VALUE;

        int searchIndent = 0;
        int cochleaLenght;
        int pixelValue;

        int i, j, destinationX, destinationY;

        boolean isFound = false;

        while (!isFound) {
            cochleaLenght = 2 * searchIndent + 1;
            if (cochleaLenght <= backGroundThreshold) {
                for (i = 0; i < cochleaLenght; i++) {

                    if ((i == 0) || (i == cochleaLenght - 1)) {//Первая и последняя строки. Интересуют все элементы строки.

                        for (j = 0; j < cochleaLenght; j++) {

                            destinationX = x - searchIndent + i;
                            destinationY = y - searchIndent + j;

                            if (checkThatPositionIsWithinImage(destinationX, destinationY, width, height)) {

                                pixelValue = secondImageDataBlock[destinationX * width + destinationY];

                                if (pixelValue > 0) {

                                    isFound = true;
                                    distance = calculateEuclideanSpace(x, y, destinationX, destinationY);
                                    if (distance < minDistance) {
                                        minDistance = distance;
                                    }

                                }
                            }

                        }
                    } else {//Промежуточные строки - когда нас интересуют только первый и последний элемент строки.
                        destinationX = x - searchIndent + i;
                        destinationY = y - searchIndent;

                        if (checkThatPositionIsWithinImage(destinationX, destinationY, width, height)) {

                            pixelValue = secondImageDataBlock[destinationX * width + destinationY];

                            if (pixelValue > 0) {

                                isFound = true;
                                distance = calculateEuclideanSpace(x, y, destinationX, destinationY);
                                if (distance < minDistance) {
                                    minDistance = distance;
                                }

                            }
                        }


                        destinationX = x - searchIndent + i;
                        destinationY = y + searchIndent;

                        if (checkThatPositionIsWithinImage(destinationX, destinationY, width, height)) {

                            pixelValue = secondImageDataBlock[destinationX * width + destinationY];

                            if (pixelValue > 0) {

                                isFound = true;
                                distance = calculateEuclideanSpace(x, y, destinationX, destinationY);
                                if (distance < minDistance) {
                                    minDistance = distance;
                                }

                            }
                        }
                    }
                }
            } else {
                isFound = true;
                minDistance = backGroundThreshold;
            }
            if (!isFound) {
                searchIndent++;
                if (!checkThatWorkIsDoingWithinImage(x, y, searchIndent, width, height)) {
//                    throw new RuntimeException("Полный выход за пределы изображения - одно из изображений пусто.");
                    return Double.MAX_VALUE;
                }
            }
        }

        return minDistance;
    }

    private boolean checkThatWorkIsDoingWithinImage(int x, int y, int indent, int widht, int height) {
        return (x - indent >= 0)//Вверх изображения
                || (y - indent >= 0)//Левая часть изображения
                || (x + indent < widht)//Низ изображения
                || (y + indent < height);//Правая часть изображения
    }

    private boolean checkThatPositionIsWithinImage(int x, int y, int widht, int height) {
        return (x >= 0)
                && (y >= 0)
                && (x < widht)
                && (y < height);
    }

    private double calculateEuclideanSpace(int beginX, int beginY, int endX, int endY) {
        return Math.sqrt(Math.pow(beginX - endX, 2) + Math.pow(beginY - endY, 2));
    }
}
